import React, { Component } from 'react';
import _ from 'underscore';
import moment from 'moment';
import numeral from 'numeral';
import cx from 'classnames';
import $ from 'jquery';

import dispatcher from 'lib/dispatcher';
import { Info } from 'lib/components/Notify.jsx';
import Select from 'lib/components/Select.jsx';
import DatePickerCore, { calendarRowCount } from 'lib/components/DatePickerCore.jsx';

export const dayList = [
  {
    value: 30*60*1000,
    text: ['30', '分钟']
  }, {
    value: 60*60*1000,
    text: ['60', '分钟']
  }, {
    value: 3*60*60*1000,
    text: ['3', '小时']
  }, {
    value: 6*60*60*1000,
    text: ['6', '小时']
  }, {
    value: 12*60*60*1000,
    text: ['12', '小时']
  }, {
    value: 24*60*60*1000,
    text: ['1', '天']
  }, {
    value: 3*24*60*60*1000,
    text: ['3', '天']
  }, {
    value: 7*24*60*60*1000,
    text: ['7', '天']
  }, {
    value: 15*24*60*60*1000,
    text: ['15', '天']
  }, {
    value: 30*24*60*60*1000,
    text: ['30', '天']
  }
];

export const hours = _.map(_.range(24), p => ({
  value: p,
  label: p < 10 ? `0${ p }` : p.toString()
}));

export const minutes = _.map(_.range(60), p => ({
  value: p,
  label: p < 10 ? `0${ p }` : p.toString()
}));

export default class DateRangePicker extends Component {
  constructor(props, context) {
    super(props, context);

    let range = props.range || 7*24*60*60*1000;
    if (range < props.spanTime) {
      throw new Error('查询时间段超过允许的最大范围！');
    }

    let data = null;
    if (!props.endTime) {
      data = _.find(dayList, p => p.value === props.spanTime);
    }

    this.state = {
      isOpen: false,
      isCustom: !data,
      range,
      data: data ? data : {
        value: props.spanTime
      },
      canSubmit: false,
      customData: null,
      errorMessage: null
    }; // state

    _.extend(this.state, this.refreshCusomData());

    if (this.state.isCustom || !this.state.data.text) {
      let { $selectedMinDay, $selectedMaxDay } = this.getSelectedDays(this.state.customData);

      let endTime = $selectedMaxDay.valueOf();
      this._lastSpanTime = endTime - $selectedMinDay.valueOf(),
      this._lastEndTime = endTime;
    } else {
      this._lastSpanTime = this.state.data.value,
      this._lastEndTime = null;
    }

    this.closeMenu = this.closeMenu.bind(this);
  } // constructor

  closeMenu({ target }) {
    if (this.state.isOpen && !$.contains(this.refs.root, target)) {
      this.setState({
        isOpen: false
      });
    }
  } // closeMenu

  toggle() {
    this.setState(({ isOpen }) => ({
      isOpen: !isOpen,
      errorMessage: null
    }));
  } // toggle

  getDefaultTimes() {
    return {
      minTime: {
        hour: 0,
        minute: 0
      },
      maxTime: {
        hour: 0,
        minute: 0
      }
    };
  } // getDefaultTimes

  refreshCusomData() {
    let monthRight = moment().startOf('month').valueOf()
      , monthLeft = moment(monthRight).subtract(1, 'months').valueOf()
      , rightRowCount = calendarRowCount(monthRight, this.props.firstColumnDay)
      , leftRowCount = calendarRowCount(monthLeft, this.props.firstColumnDay)
      , rowCount = _.max([rightRowCount, leftRowCount])
      , maxDate = moment().startOf('day').valueOf()
      , minDate = maxDate - this.state.range
      , selectedMaxDay = this.props.endTime || Date.now()
      , selectedMinDay = selectedMaxDay - this.state.data.value
      , maxStartDay = moment(selectedMaxDay).startOf('day').valueOf()
      , minStartDay = moment(selectedMinDay).startOf('day').valueOf()
      , maxDuration = moment.duration(selectedMaxDay - maxStartDay)
      , minDuration = moment.duration(selectedMinDay - minStartDay);

    return {
      customData: {
        minDate,
        maxDate,
        monthLeft,
        monthRight,
        rowCount,
        selectedMaxDay: maxStartDay,
        selectedMinDay: minStartDay
      },
      maxTime: {
        hour: maxDuration.hours(),
        minute: maxDuration.minutes()
      },
      minTime: {
        hour: minDuration.hours(),
        minute: minDuration.minutes()
      }
    };
  } // refreshCusomData

  toggleCustom() {
    if (this.state.isCustom) {
      return;
    }

    this.setState(({ isCustom }) => _.extend(this.refreshCusomData(), {
      isCustom: !isCustom
    }));
  } // toggleCustom

  getText() {
    if (this.state.isCustom || !this.state.data.text) {
      let { $selectedMinDay, $selectedMaxDay } = this.getSelectedDays(this.state.customData);

      return `${ this.getFormattedDate($selectedMinDay) } ~ ${ this.getFormattedDate($selectedMaxDay) }`;
    } else {
      return `最新 ${ this.state.data.text.join(' ') }`;
    }
  } // getText

  selectPredefinedItem (item, { target: { classList }}) {
    if (_.contains(classList, 'active')) {
      this.setState({
        isOpen: false
      });
    } else {
      this.setState(_.extend(this.getDefaultTimes(), {
        isOpen: false,
        isCustom: false,
        data: item
      }), this.onCompleted);
    }
  } // selectPredefinedItem

  customDisabledHandler(item) {
    if (item < this.state.customData.minDate) {
      return true;
    }

    if (item > this.state.customData.maxDate) {
      return true;
    }

    return false;
  } // customDisabledHandler

  movingPreviousHandler(item) {
    this.setState(({ customData }) => ({
      customData: _.extend(customData, {
        monthRight: customData.monthLeft,
        monthLeft: moment(customData.monthLeft).subtract(1, 'months').valueOf()
      })
    }));
  } // movingPreviousHandler

  movingNextHandler(item) {
    this.setState(({ customData }) => ({
      customData: _.extend(customData, {
        monthLeft: customData.monthRight,
        monthRight: moment(customData.monthRight).add(1, 'months').valueOf()
      })
    }));
  } // movingNextHandler

  selectingHandler(item) {
    let customData = this.state.customData;

    if (item < customData.selectedMinDay) {
      this.setState(({ customData }) => ({
        customData: _.extend(customData, {
          selectedMinDay: item
        })
      }));
    } else if (item > customData.selectedMaxDay) {
      this.setState(({ customData }) => ({
        customData: _.extend(customData, {
          selectedMaxDay: item
        })
      }));
    } else if ((item > customData.selectedMinDay && item < customData.selectedMaxDay) || item === customData.selectedMinDay || item === customData.selectedMaxDay) {
      this.setState(({ customData }) => _.extend(this.getDefaultTimes(), {
        customData: _.extend(customData, {
          selectedMinDay: item,
          selectedMaxDay: item
        }),
        maxTime: {
          hour: 23,
          minute: 59
        }
      }));
    }
  } // selectingHandler

  changeMinTimeHandler(key, value) {
    this.setState(({ minTime }) => ({
      minTime: _.extend(minTime, {
        [key]: value.value
      })
    }))
  } // changeMinTimeHandler

  changeMaxTimeHandler(key, value) {
    this.setState(({ maxTime }) => ({
      maxTime: _.extend(maxTime, {
        [key]: value.value
      })
    }))
  } // changeMaxTimeHandler

  getFormattedDate($value) {
    if (this.props.useTime) {
      return $value.format('YYYY-MM-DD HH:mm');
    } else {
      return $value.format('YYYY-MM-DD');
    }
  } // getFormattedDate

  cancelHandler() {
    this.setState(() => ({
      isCustom: false
    }));
  } // cancelHandler

  applyHandler(margin) {
    if (this.state.canSubmit) {
      this.setState({
        isOpen: false,
        errorMessage: null
      }, this.onCompleted);
    } else {
      let errorMessage = margin < this.props.min ? '当前所选时间小于最小可选时间30分钟' : '当前所选时间大于最大可选时间' + moment(this.state.range).humanize();

      this.setState({
        errorMessage
      }, this.resetTooltip);
    }
  } // applyHandler

  resetTooltip() {
    if (this.__timeout) {
      clearTimeout(this.__timeout);
    }

    this.__timeout = setTimeout(() => this.setState({
      errorMessage: null
    }), 2000);
  } // resetTooltip

  getMinutes(timestamp) {
    return timestamp / 60000;
  }

  onCompleted() {
    let onCompleted = this.props.onCompleted;
    if (onCompleted) {
      let parameter = null;
      if (this.state.isCustom || !this.state.data.text) {
        let { $selectedMinDay, $selectedMaxDay } = this.getSelectedDays(this.state.customData);
        if (!$selectedMaxDay.isAfter($selectedMinDay)) {
          Info('结束时间必须大于开始时间！');
          return;
        }

        let endTime = $selectedMaxDay.valueOf();
        parameter = {
          spanTime: endTime - $selectedMinDay.valueOf(),
          endTime
        };
      } else {
        parameter = {
          spanTime: this.state.data.value,
          endTime: null
        };
      }

      if (this.state.range < parameter.spanTime) {
        Info('查询时间段超过允许的最大范围！');
        return;
      }

      if (this._lastSpanTime !== parameter.spanTime ||
          this._lastEndTime !== parameter.endTime) {
        this._lastSpanTime = parameter.spanTime;
        this._lastEndTime = parameter.endTime;

        onCompleted(parameter);
      }
    }
  }

  getSelectedDays(customData = this.state.customData) {
    let $selectedMinDay = moment(customData.selectedMinDay)
      , $selectedMaxDay = moment(customData.selectedMaxDay);
    if (this.props.useTime) {
      $selectedMinDay = $selectedMinDay.add(this.state.minTime.hour, 'hours')
                                       .add(this.state.minTime.minute, 'minutes');
      $selectedMaxDay = $selectedMaxDay.add(this.state.maxTime.hour, 'hours')
                                       .add(this.state.maxTime.minute, 'minutes');
    }

    return {
      $selectedMinDay,
      $selectedMaxDay
    };
  } // getSelectedDays

  componentDidMount() {
    $('body').on('click', this.closeMenu);
  } // componentDidMount

  componentWillUnmount() {
    $('body').off('click', this.closeMenu);

    if (this.__timeout) {
      clearTimeout(this.__timeout);
    }
  } // componentWillUnmount

  renderCustomBody() {
    let customData = this.state.customData
      , canMovePrevious = moment(customData.monthLeft).subtract(1, 'months').endOf('month').valueOf() > customData.minDate
      , canMoveNext = moment(customData.monthRight).add(1, 'months').valueOf() < customData.maxDate
      , { $selectedMinDay, $selectedMaxDay } = this.getSelectedDays(customData)
      , margin = this.getMinutes($selectedMaxDay) - this.getMinutes($selectedMinDay); // 求分钟的差值，判断是否超出指定时间范围

    this.state.canSubmit = margin >= this.props.min && margin <= this.props.max;

    return (
      <div>
        <div className="pure-g date-picker-customer">
          <div className="pure-u-1 text-align-center">
            <DatePickerCore
              rowCount={ customData.rowCount }
              firstColumnDay={ this.props.firstColumnDay }
              monthValue={ customData.monthLeft }
              canMoveNext={ false }
              style={{ marginRight: 15 }}
              disabledCallback={ this.customDisabledHandler.bind(this) }
              canMovePrevious={ canMovePrevious }
              onMovingPrevious={ this.movingPreviousHandler.bind(this) }
              selectedMinDay={ customData.selectedMinDay }
              selectedMaxDay={ customData.selectedMaxDay }
              onSelectingDay={ this.selectingHandler.bind(this) }
            />
            <DatePickerCore
              rowCount={ customData.rowCount }
              firstColumnDay={ this.props.firstColumnDay }
              monthValue={ customData.monthRight }
              canMovePrevious={ false }
              style={{ marginLeft: 15 }}
              disabledCallback={ this.customDisabledHandler.bind(this) }
              canMoveNext={ canMoveNext }
              onMovingNext={ this.movingNextHandler.bind(this) }
              selectedMinDay={ customData.selectedMinDay }
              selectedMaxDay={ customData.selectedMaxDay }
              onSelectingDay={ this.selectingHandler.bind(this) }
            />
          </div>
          {
            !this.props.useTime ? null : (
              <div className="pure-u-1 text-align-center line"></div>
            )
          }
          {
            !this.props.useTime ? null : (
              <div className="pure-u-1 text-align-center time-row">
                <div className="float-left">
                  <input
                    type="text"
                    className="input-text date-displayer"
                    readOnly={ true }
                    value={ moment(customData.selectedMinDay).format('YYYY-MM-DD') } />
                  <Select
                    value={ _.find(hours, p => this.state.minTime.hour === p.value) }
                    options={ hours }
                    style={{ margin: 'auto 10px' }}
                    onChange={ this.changeMinTimeHandler.bind(this, 'hour') } />
                  <Select
                    value={ _.find(minutes, p => this.state.minTime.minute === p.value) }
                    options={ minutes }
                    onChange={ this.changeMinTimeHandler.bind(this, 'minute') } />
                </div>
                <div className="spliter">至</div>
                <div className="float-right">
                  <input
                    type="text"
                    className="input-text date-displayer"
                    readOnly={ true }
                    value={ moment(customData.selectedMaxDay).format('YYYY-MM-DD') } />
                  <Select
                    value={ _.find(hours, p => this.state.maxTime.hour === p.value) }
                    options={ hours }
                    style={{ margin: 'auto 10px' }}
                    onChange={ this.changeMaxTimeHandler.bind(this, 'hour') } />
                  <Select
                    value={ _.find(minutes, p => this.state.maxTime.minute === p.value) }
                    options={ minutes }
                    onChange={ this.changeMaxTimeHandler.bind(this, 'minute') } />
                </div>
              </div>
            )
          }
        </div>
        <div className="date-picker-footer">
          <div className="float-right">
            <div className="text">
              { this.state.errorMessage ? <span style={{color: '#E5603B'}}>{ this.state.errorMessage }</span> :
                  `查询 ${ this.getFormattedDate($selectedMinDay) } 至 ${ this.getFormattedDate($selectedMaxDay) } 内的性能数据` }
            </div>
            <button className="btn-blue mlr10" onClick={ this.cancelHandler.bind(this) }>取消</button>
            <button className="btn-green" onClick={ this.applyHandler.bind(this, margin) }>确定</button>
          </div>
        </div>
      </div>
    );
  } // renderCustomBody

  renderBody() {
    return (
      <div className="timepicker-dropdown-main date-picker-container">
        <ul style={{ marginBottom: 15 }}>
          {
            _.chain(dayList).filter(p => p.value <= this.props.range).map(item => {
              return (
                <li
                  key={ item.value }
                  onClick={ this.selectPredefinedItem.bind(this, item) }
                  className={ cx({
                    'active': !this.state.isCustom && item.value === this.state.data.value
                  }) }>
                  { item.text.join(' ') }
                </li>
              );
            }).value()
          }
          <li
            onClick={ this.toggleCustom.bind(this) }
            className={ cx({
              'active': this.state.isCustom
            }) }>
            自定义
          </li>
        </ul>
        { this.state.isCustom ? this.renderCustomBody() : null }
      </div>
    );
  } // renderBody

  render() {
    return (
      <div className="dropdown time-slider" ref="root">
        <strong onClick={ this.toggle.bind(this) }>
          <i className="iconfont icon-clock"></i>
          { this.getText() }
          <i className="iconfont icon-btn-arrow">&#xe619;</i>
        </strong>
        { this.state.isOpen ? this.renderBody() : null }
      </div>
    );
  } // render
}

DateRangePicker.defaultProps = {
  useTime: true,        // 默认添加时间选择
  spanTime: 30*60*1000, // 缺省使用 30 分钟
  endTime: null,        // null 表示 Date.now()
  range: null,          // 表示允许查看的最大时间段，30 分钟对应的值为 30*60*1000
  min: 30,              // 最小可选时间（分钟）
  max: 60 * 24 * 30,    // 最大可选时间（分钟）
  firstColumnDay: 0,    // 日历控件第一天是星期几：0 表示星期日，1 表示星期一，依此类推
  onCompleted: null     // (spanTime, endTime) => {}
}
